package com.xyzwps.boofcv;

import boofcv.abst.distort.FDistort;
import boofcv.alg.drawing.FiducialRenderEngine;
import boofcv.alg.misc.ImageMiscOps;
import boofcv.core.image.ConvertImage;
import boofcv.struct.image.GrayF32;
import boofcv.struct.image.GrayU8;
import georegression.struct.point.Point2D_F64;

public class MyImageEngine extends FiducialRenderEngine {
    protected GrayU8 gray = new GrayU8(1, 1);

    // number of pixels in the border
    private int borderPixels;

    // value of the two color squares
    private int white = 255;
    private int black = 0;

    // What color it will draw shapes with
    private int drawColor = black;

    /**
     * Specifies image size and the border.
     *
     * @param borderPixels size of white border around document
     * @param markerPixels size of workable region inside the document
     */
    public void configure(int borderPixels, int markerPixels) {
        this.borderPixels = borderPixels;

        int width = markerPixels + 2 * borderPixels;
        gray.reshape(width, width);
    }

    public void configure(int borderPixels, int markerWidth, int markerHeight) {
        this.borderPixels = borderPixels;

        int width = markerWidth + 2 * borderPixels;
        int height = markerHeight + 2 * borderPixels;
        gray.reshape(width, height);
    }

    @Override
    public void init() {
        ImageMiscOps.fill(gray, white);
    }

    @Override
    public void setGray(double value) {
        drawColor = (int) (255 * value);
    }

    @Override
    public void circle(double cx, double cy, double radius) {
        int x0 = borderPixels + (int) Math.round(cx - radius);
        int y0 = borderPixels + (int) Math.round(cy - radius);
        int x1 = borderPixels + (int) Math.round(cx + radius) + 1;
        int y1 = borderPixels + (int) Math.round(cy + radius) + 1;

        // border adjusted center
        double bcx = borderPixels + cx;
        double bcy = borderPixels + cy;

        // bound it inside the image
        x0 = Math.max(0, x0);
        y0 = Math.max(0, y0);
        x1 = Math.min(gray.width, x1);
        y1 = Math.min(gray.height, y1);

        // Brute force circle filling algorithm
        double r2 = radius * radius;
        for (int y = y0; y < y1; y++) {
            double dy = y - bcy;
            for (int x = x0; x < x1; x++) {
                double dx = x - bcx;
                if (dx * dx + dy * dy <= r2)
                    gray.unsafe_set(x, y, drawColor);
            }
        }
    }

    /**
     * 绘制具体的点
     */
    @Override
    public void rectangle(double x0, double y0, double x1, double y1) {
        int pixelX0 = borderPixels + (int) (x0 + 0.5 + 0.5);
        int pixelY0 = borderPixels + (int) (y0 + 0.5 + 0.5);
        int pixelX1 = borderPixels + (int) (x1 + 0.5 - 0.5);
        int pixelY1 = borderPixels + (int) (y1 + 0.5 - 0.5);

        ImageMiscOps.fillRectangle(gray, 0b11000011, pixelX0, pixelY0, pixelX1 - pixelX0, pixelY1 - pixelY0);
    }


    /**
     * 绘制左上角、左下角、右上角的 position 方格
     */
    @Override
    public void square(double x0, double y0, double width0, double thickness) {

        int X0 = borderPixels + (int) (x0 + 0.5);
        int Y0 = borderPixels + (int) (y0 + 0.5);
        int WIDTH = (int) (width0 + 0.5);
        int THICKNESS = (int) (thickness + 0.5);

        ImageMiscOps.fillRectangle(gray, drawColor, X0, Y0, WIDTH, THICKNESS);
        ImageMiscOps.fillRectangle(gray, drawColor, X0, Y0 + WIDTH - THICKNESS, WIDTH, THICKNESS);
        ImageMiscOps.fillRectangle(gray, drawColor, X0, Y0 + THICKNESS, THICKNESS, WIDTH - THICKNESS * 2);
        ImageMiscOps.fillRectangle(gray, drawColor, X0 + WIDTH - THICKNESS, Y0 + THICKNESS, THICKNESS, WIDTH - THICKNESS * 2);
    }

    @Override
    public void draw(GrayU8 image, double x0, double y0, double x1, double y1) {
        int X0 = borderPixels + (int) (x0 + 0.5);
        int Y0 = borderPixels + (int) (y0 + 0.5);
        int X1 = borderPixels + (int) (x1 + 0.5);
        int Y1 = borderPixels + (int) (y1 + 0.5);

        GrayU8 out = new GrayU8(X1 - X0, Y1 - Y0);
        new FDistort(image, out).scale().apply();

        gray.subimage(X0, Y0, X1, Y1).setTo(out);
    }

    @Override
    public void inputToDocument(double x, double y, Point2D_F64 document) {
        document.x = x + borderPixels;
        document.y = y + borderPixels;
    }

    public int getBorderPixels() {
        return borderPixels;
    }

    public GrayU8 getGray() {
        return gray;
    }

    public GrayF32 getGrayF32() {
        GrayF32 out = new GrayF32(gray.width, gray.height);
        ConvertImage.convert(gray, out);
        return out;
    }

    public int getWhite() {
        return white;
    }

    public void setWhite(int white) {
        this.white = white;
    }

    public int getBlack() {
        return black;
    }

    public void setBlack(int black) {
        this.black = black;
    }
}
